/****************************************************************************
 * datapath.v
 ****************************************************************************/
`include "defines.v"

/****************************************************************************
   Module: datapath

   dpath模块是指令运算数据通路模块。
   dpath模块对单周期定点运算指令、跳转指令、控制类指令进行运算。
   1. 对于加法、减法、比较类指令均会共用加法器；
   2. 对于移位类指令、部分需要立即数扩展类指令会共用移位器；
   3. 对于简单逻辑运算AND、OR直接计算，不共用资源；
   4. 对于乘法，使用单独单周期乘法器，不共用资源；
   5. 对于异或运算、会用于等于不等于的比较运算。

 ****************************************************************************/

module datapath
( 
    input                                   rst,               // 系统复位信号，同步复位，高电平有效
    input                                   clk,               // 系统工作时钟

    input   [`REG_WIDTH*2-1 : 0]            op1,               // 计算操作数1，经过合并处理的结果，imm/reg
    input   [`REG_WIDTH*2-1 : 0]            op2,               // 计算操作数2，经过合并处理的结果，imm/reg
    input   [`REG_WIDTH-1 : 0]              csr_in,            // csr寄存器当前的值
    input   [`REG_WIDTH-1 : 0]              pc,                // 当前指令的pc值

    input                                   rglr_en,           // 普通运算指令单元
    input                                   bjp_en,            // 分支预测指令单元
    input                                   csr_en,            // CSR读写指令单元
    input                                   ctrl_en,           // 控制类指令单元
    input   [`INSTR_INFO_BUS_WIDTH-1 : 0]   instr_info,        // 解析出来的具体的指令信息

    output  [`REG_WIDTH-1 : 0]              csr_out,           // csr寄存器结果输出值
    output  [`REG_WIDTH*2-1 : 0]            reg_data,          // 寄存器结果
    output                                  done,              // HLT指令置位done信号，代表程序执行完毕，CPU状态复位
    output                                  taken,             // 跳转执行结果，
    output  [`CODE_ADDR_WIDTH-1 : 0]        bjp_dest_addr,     // 跳转目的地址
    output                                  ifu_block          // 阻塞取指信号
)

    // 指令展开，带门控
    wire regr_add  = rglr_en & instr_info[`INSTR_INFO_RGLR_ADD];
    wire regr_sub  = rglr_en & instr_info[`INSTR_INFO_RGLR_SUB];
    wire regr_slt  = rglr_en & instr_info[`INSTR_INFO_RGLR_SLT];
    wire regr_sltu = rglr_en & instr_info[`INSTR_INFO_RGLR_SLTU];
    wire regr_sll  = rglr_en & instr_info[`INSTR_INFO_RGLR_SLL];
    wire regr_srl  = rglr_en & instr_info[`INSTR_INFO_RGLR_SRL];
    wire regr_sra  = rglr_en & instr_info[`INSTR_INFO_RGLR_SRA];
    wire regr_xor  = rglr_en & instr_info[`INSTR_INFO_RGLR_XOR];
    wire regr_or   = rglr_en & instr_info[`INSTR_INFO_RGLR_OR];
    wire regr_and  = rglr_en & instr_info[`INSTR_INFO_RGLR_AND];
    wire regr_mul  = rglr_en & instr_info[`INSTR_INFO_RGLR_MUL];
    wire regr_mulu = rglr_en & instr_info[`INSTR_INFO_RGLR_MULU];

    wire bjp_jalr  = bjp_en  & instr_info[`INSTR_INFO_BJP_JALR];
    wire bjp_jal   = bjp_en  & instr_info[`INSTR_INFO_BJP_JAL];
    wire bjp_beq   = bjp_en  & instr_info[`INSTR_INFO_BJP_BEQ];
    wire bjp_bne   = bjp_en  & instr_info[`INSTR_INFO_BJP_BNE];
    wire bjp_blt   = bjp_en  & instr_info[`INSTR_INFO_BJP_BLT];
    wire bjp_bge   = bjp_en  & instr_info[`INSTR_INFO_BJP_BGE];

    wire csr_csrrs = csr_en  & instr_info[`INSTR_INFO_CSR_CSRRS];
    wire csr_csrrc = csr_en  & instr_info[`INSTR_INFO_CSR_CSRRC];
    wire csr_csrrw = csr_en  & instr_info[`INSTR_INFO_CSR_CSRRW];
    wire csr_csrrr = csr_en  & instr_info[`INSTR_INFO_CSR_CSRRR];

    wire ctrl_hlt  = ctrl_en & instr_info[`INSTR_INFO_CTRL_HLT];
    wire ctrl_lui  = ctrl_en & instr_info[`INSTR_INFO_CTRL_LUI];


    /*----------------------------------------------------------------------------
        加减计算模块实现，此单元的位宽比寄存器位宽大1bit；
        加减计算模块会被如下指令利用：add、sub、slt、sltu，add指令进行加法运算，sub、slt、sltu需要转换成减法运算;
        有符号和无符号共用同一个计算单元，对于有符号和无符号计算输入数据需要做不同的扩展处理（扩展1bit），无符号扩展0，有符号扩展符号位；
    */
    // 首先根据指令的符号类型进行处理操作数的扩展处理
    wire op_unsigned = regr_sltu;
    wire [`REG_WIDTH : 0] adder_op1 = {(~op_unsigned) & op1[`REG_WIDTH-1], op1[`REG_WIDTH-1 : 0]};
    wire [`REG_WIDTH : 0] adder_op2 = {(~op_unsigned) & op2[`REG_WIDTH-1], op2[`REG_WIDTH-1 : 0]};

    // 判断运算类型是加法运算还是减法运算，如果是减法运算，需要对操op2做求补码运算，对输入数据需要做门控处理以节能
    wire adder_add = regr_add;
    wire adder_sub = regr_sub | regr_slt | regr_sltu;
    wire adder_addsub = adder_add | adder_sub;
    wire [`REG_WIDTH : 0] adder_in1 = {`REG_WIDTH{adder_addsub}} & adder_op1;
    wire [`REG_WIDTH : 0] adder_in2 = {`REG_WIDTH{adder_addsub}} & (adder_sub ? (~adder_op2) : adder_op2);
    wire adder_cin = adder_addsub & adder_sub;
    wire [`REG_WIDTH : 0] adder_res = adder_in1 + adder_in2 + adder_cin;

    // 计算结果状态值
    wire adder_csr_ne  = (!adder_res);
    wire adder_csr_eq =  ~adder_csr_eq;
    wire adder_csr_lt  = adder_res[`REG_WIDTH];
    wire adder_csr_ge  = ~adder_res[`REG_WIDTH];


    /*----------------------------------------------------------------------------
        移位类指令实现，此单元的位宽为32bits；
        移位之所以设计为32位，是为了便于乘法运算的处理方便：
            例如两个16位Q12的数进行乘法，可以通过x1 * x2 >> 12，简便的实现低精度浮点的乘法运算。
        移位类指令有：sll、srl、sra
    */
    wire op_shift = regr_sll | regr_srl | regr_sra;

    // 根据是左移还是右移对操作数一做反转处理，以便共用移位器，真正的执行单元只有一个左移器
    // 增加门控以节能
    wire [`REG_WIDTH*2-1 : 0] shifter_in1 = {(`REG_WIDTH*2){op_shift}}  & (regr_sll ? op1 :
                                        {op1[00],[01],[02],op1[03],op1[04],[05],[06],op1[07],
                                            op1[08],[09],[10],op1[11],op1[12],[13],[14],op1[15],
                                            op1[16],[17],[18],op1[19],op1[20],[21],[22],op1[23],
                                            op1[24],[25],[26],op1[27],op1[28],[29],[30],op1[31]});
    wire [4 : 0]              shifter_in2 = {5{op_shift}} & op2[4 : 0];

    // 移位运算
    wire [`REG_WIDTH*2-1 : 0] shifter_res = (shifter_in1 << shifter_in2);

    // 逻辑左移的运算结果
    wire [`REG_WIDTH*2-1 : 0] sll_res = shifter_res;
    // 逻辑右移的运算结果
    wire [`REG_WIDTH*2-1 : 0] srl_res = { shifter_res[00],shifter_res[01],shifter_res[02],shifter_res[03],
                                        shifter_res[04],shifter_res[05],shifter_res[06],shifter_res[07],
                                        shifter_res[08],shifter_res[09],shifter_res[10],shifter_res[11],
                                        shifter_res[12],shifter_res[13],shifter_res[14],shifter_res[15],
                                        shifter_res[16],shifter_res[17],shifter_res[18],shifter_res[19],
                                        shifter_res[20],shifter_res[21],shifter_res[22],shifter_res[23],
                                        shifter_res[24],shifter_res[25],shifter_res[26],shifter_res[27],
                                        shifter_res[28],shifter_res[29],shifter_res[30],shifter_res[31] };
    // 算数右移的运算结果
    wire [`REG_WIDTH*2-1 : 0] eff_mask = ((`REG_WIDTH*2)'b1) >> shifter_in2;
    wire [`REG_WIDTH*2-1 : 0] sra_res  = srl_res | ({32{op1[31]}} & (~eff_mask));


    /*----------------------------------------------------------------------------
        异或指令实现，此单元位宽与寄存器位宽一致
        异或指令模块也可以用来更新csr寄存器的状态位，可以实现相等和不相等的比较运算，
        相比加法器电路更加简单，因此更加节能。所以对于某些只需要比较相等和不等的运算可以使用此模块。
    */
    // 对输入数据做门控处理，节能
    wire xorer_op = regr_xor;
    wire [`REG_WIDTH-1 : 0] xorer_in1 = {`REG_WIDTH{xorer_op}} & op1[`REG_WIDTH-1 : 0];
    wire [`REG_WIDTH-1 : 0] xorer_in2 = {`REG_WIDTH{xorer_op}} & op2[`REG_WIDTH-1 : 0];

    // 异或运算
    wire [`REG_WIDTH-1 : 0] xorer_res = xorer_in1 ^ xorer_in2;
    wire xorer_csr_ne = (|xorer_res);
    wire xorer_csr_eq = ~xorer_csr_ne;


    /*----------------------------------------------------------------------------
        and、or逻辑运算比较简单，直接进行计算即可
    */
    wire [`REG_WIDTH-1 : 0] orer_res  = op1[`REG_WIDTH-1 : 0] | op2[`REG_WIDTH-1 : 0];
    wire [`REG_WIDTH-1 : 0] ander_res = op1[`REG_WIDTH-1 : 0] & op2[`REG_WIDTH-1 : 0];


    /*----------------------------------------------------------------------------
        乘法类指令运算实现
        TODO
    */
    wire op_mult = regr_mul | regr_mulu;


    /*----------------------------------------------------------------------------
        跳转类指令运算实现
    */
    assign taken = bjp_en & (
                    bjp_jalr | 
                    bjp_jal |
                    (bjp_beq & csr_in[`CSR_BIT_EQ]) |
                    (bjp_bne & csr_in[`CSR_BIT_NE]) |
                    (bjp_blt & csr_in[`CSR_BIT_LT]) |
                    (bjp_bge & csr_in[`CSR_BIT_GE]) 
                    );
    assign ifu_block     = bjp_en & (bjp_beq | bjp_bne | bjp_blt | bjp_bge);
    assign bjp_dest_addr = op1[`CODE_ADDR_WIDTH-1 : 0];
    // TODO 此处可以优化，节约面积，或者和加法器复用计算电路;
    // 或者和ifu里面的地址生成部分复用计算电路，此时结果含义变化位当前PC，而不是PC+1
    wire   bjp_res       = {`REG_WIDTH{bjp_jal | bjp_jalr}} & (PC + 1); 


    /*----------------------------------------------------------------------------
        控制类指令运算实现
    */
    assign done = ctrl_hlt;
    assign ctrl_res = {`REG_WIDTH{ctrl_lui}} & op1[`REG_WIDTH-1 : 0];

    /*----------------------------------------------------------------------------
        CSR读写类指令运算实现
    */
    csr_bit_op = csr_csrrs | csr_csrrc;
    wire [3 : 1] csr_bit_index = {4{csr_bit_op} & op1[3 : 1];
    // TODO 此处可以优化，与移位器复用计算电路节约面积
    wire [`REG_WIDTH-1 : 0] csr_mask = {(`REG_WIDTH-1){1'b0}, csr_bit_op} << csr_bit_index
    wire [`REG_WIDTH-1 : 0] csr_dat  = csr_csrrs ? (csr_mask    | csr_in) :
                                    csr_csrrc ? ((~csr_mask) & csr_in) :
                                    op1[`REG_WIDTH-1 : 0]; // csrrw
    wire [`REG_WIDTH-1 : 0] csr_res  = csr_in;                // csrrr


    /*----------------------------------------------------------------------------
        将数据通路计算的结果汇总输出
    */
    assign csr_out = adder_addsub ? {csr_in[`REG_WIDTH-1 : 4], adder_csr_ge, adder_csr_lt, adder_csr_ne, adder_csr_eq} :
                    xorer_op     ? {csr_in[`REG_WIDTH-1 : 2], xorer_csr_ne, xorer_csr_eq} :
                    (csr_bit_op | csr_csrrw) ? csr_dat : csr_in;

    assign reg_data[`REG_WIDTH-1 : 0] = adder_addsub ? adder_res[`REG_WIDTH-1 : 0] :
                                        regr_sll     ? sll_res[`REG_WIDTH-1 : 0] :
                                        regr_srl     ? srl_res[`REG_WIDTH-1 : 0] :
                                        regr_sra     ? sra_res[`REG_WIDTH-1 : 0] :
                                        xorer_op     ? xorer_res :
                                        regr_and     ? adder_res :
                                        regr_or      ? orer_res :
                                        (bjp_jal | bjp_jalr) ? bjp_res :
                                        ctrl_lui     ? ctrl_res :
                                        csr_csrrr    ? csr_res :
                                        {`REG_WIDTH{1'b0}};

    assign reg_data[`REG_WIDTH*2-1 : `REG_WIDTH] = regr_sll ? sll_res[`REG_WIDTH*2-1 : `REG_WIDTH] :
                                                regr_srl ? srl_res[`REG_WIDTH*2-1 : `REG_WIDTH] :
                                                regr_sra ? sra_res[`REG_WIDTH*2-1 : `REG_WIDTH] :
                                                {`REG_WIDTH{1'b0}};


endmodule